package gri2_requests;

use DBI;
use Data::Dumper;
use File::Basename;
use MIME::Base64;
use gri_portal_config;

############################################################################
# Request fields are as follows....
# id   - Unique request ID
# type - PAR  - Portal access Request
#        PARR - Portal access revocation Request
# user - User id requesting the change
# object - the name of the obect for the access change (portal name usually)
#          base64 encoded.
# req_comments - Requestors original comments - base64 encoded.
# owners_votes - uid|Y or N|comments ... each item '|' separated. Comments 
#                are base64 encoded.
# voters_votes - same format as above, but for voters, not owners.
# decision     - Allow/Dissallow - if entry here record is closed...
# concurrence   - number of user or % of voters that must allow access
# informed     - Has used been informed
# comments     - General comments - base64 encoded.
#
############################################################################

# Decision making process (for portals for example)
# Any owner can instantly pass/fail access - take precedence over voters.
# Voters go towards the concurance count
# If decision is made then field set and user informed, and information
# added to comments.
#

my $cldb_errcode=0;
my $cldb_errstr=undef;

sub _exists($$$) {
my $self=shift;
my ($type,$obj)=@_;
my ($cmd,$rc,$sth);

	$cldb_errcode=0;
	$cldb_errstr=undef;
	$type="table" if !defined($type);
	$cmd="SELECT * FROM SQLITE_MASTER WHERE type = '$type' AND name = '$obj';";
	my $h=$self->{dbh}->selectrow_hashref("$cmd");
	if(!defined($h)) {
		return 0;
	}
	if(!defined($h)) {
		return 0;
	}
	return 1;
}

sub qs($$) {
my $self=shift;
my $s=shift;

	return $self->{dbh}->quote($s);
}

sub get_dbh($) {
	return $_[0]->{dbh};
}

sub do($$;$) {
my $self=shift;
my $timeout=$_[2];

	$timeout=3 if !defined($timeout);
	$cldb_errcode=0;
	$cldb_errstr=undef;
	my $ctime=0;
	while($ctime<=$timeout) {
		$self->{dbh}->do($_[0]);
		if(defined($self->{dbh}->err) && $self->{dbh}->err==5) {
			$cldb_errcode=5;
			$cldb_errstr=$self->{dbh}->errstr;
			# locked error;
			$ctime+=0.10;
			select(undef,undef,undef,0.10);
			next;
		}
		my $rc=$self->{dbh}->errstr;
		if(defined($rc)) {
			$cldb_errcode=2;
			$cldb_errstr=$rc;
			return 0;
		}
		return 1;
	}
	return 0;
}

sub get_rows($$) {
my ($self,$query)=@_;
my ($sth,$rc,@R,$aref);

	$cldb_errcode=0;
	$cldb_errstr=undef;
	$sth=$self->{dbh}->prepare($query);
	if(!defined($sth)) {
		$rc=$self->{dbh}->errstr;
		$cldb_errcode=5;
		$cldb_errstr=$rc;
		return undef;
	}
	$sth->execute();
	@R=();
	while($aref  = $sth->fetchrow_hashref) {
		push @R,{%$aref};
	}
	return \@R;
}

sub new {
my $proto = shift;
my $class = ref($proto) || $proto;
my $self  = {};
my $dbname=$_[0];

	$self->{dbname}=$dbname;
	my $dir=dirname($dbname);
	if(! -w $dir) {
		print STDERR "ERROR: The directory '$dir' is not writable by UID '$<'.\n";
		return undef;

	}
	$self->{dbh}=DBI->connect("dbi:SQLite:dbname=$dbname","","",
		{RaiseError => 1,PrintError => 1,AutoCommit => 1});

	
	bless($self,$class);

	# Create the doc_index table if necessary
	if(! _exists($self,"table","requests")) {
		my $r=$self->do("CREATE TABLE requests(
			id   CHAR(30),
			type CHAR(10),
            user CHAR(30),
            object CHAR(200),
            req_comments CHAR(1000),
            owners_votes CHAR(1000),
            voters_votes CHAR(1000),
            decision CHAR(10),
            concurrence CHAR(10),
            informed CHAR(1),
            comments CHAR(1000) );");

		if(!$r) {
			return undef;
		}
	}
	return $self;
}

sub error {
	return($cldb_errcode,$cldb_errstr);
}


sub _contains_field($$$) {
my ($dbh,$obj,$field)=@_;
my ($cmd,$rc,$sth);

	$cmd="SELECT sql FROM SQLITE_MASTER WHERE type = 'table' AND name = '$obj';";
	my $h=$dbh->selectrow_hashref("$cmd");
	if(!defined($h)) {
		return -1; # table does not exist
	}
	my @F;
	if ($h->{sql} =~ /\n/) {
		@F=split(/\n/,$h->{sql});
	} else {
		my $xx=$h->{sql};
		$xx =~ s/^CREATE TABLE $obj\(//;
		$xx =~ s/\)$//;
		@F=("junk",split(/,/,$xx),"junk");
	}
	for my $cl (@F[1 .. $#F-1]) {
		my $y=$cl;
		$y =~ s/^\s+//;
		my @F2=split(/\s+/,$y);
		if(lc($F2[0]) eq lc($field)) {
			return 1; # field already exists
		}
	}
	return 0; # field does not exist
}

sub _table_field_list($$) {
my ($dbh,$table)=@_;
my ($cmd,$rc,$sth);

	$cmd="SELECT sql FROM SQLITE_MASTER WHERE type = 'table' AND name = '$table';";
	my $h=$dbh->selectrow_hashref("$cmd");
	if(!defined($h)) {
		return (0,[],[]); # table does not exist
	}
	my @F;
	if ($h->{sql} =~ /\n/) {
		@F=split(/\n/,$h->{sql});
	} else {
		my $xx=$h->{sql};
		$xx =~ s/^CREATE TABLE $table\(//;
		$xx =~ s/\)$//;
		@F=("junk",split(/,/,$xx),"junk");
	}
	my @FF=();
	my @FF2=();
	for my $cl (@F[1 .. $#F-1]) {
		my $y=$cl;
		$y =~ s/^\s+//;
		$y =~ s/,$//;
		my @YY=split(/\s+/,$y);
		push @FF,$YY[0];
		push @FF2,$YY[1];
	}
	return(1,[@FF],[@FF2]);
}

sub _get_rows($$) {
my ($dbh,$query)=@_;
my ($sth,$rc,@R,$aref);

	$sth=$dbh->prepare($query);
	if(!defined($sth)) {
		$rc=$dbh->errstr;
		return undef;
	}
	$sth->execute();
	@R=();
	while($aref  = $sth->fetchrow_hashref) {
		push @R,{%$aref};
	}
	return \@R;
}

# Returns:								#
# -1 = specified table does not exist.					#
# -2 = unable to get field definitions for table.			#
# 1  = field already present; no work required.				#
# 10+ - field added, r-10 records updated.				#

sub _add_field($$$$$) {
my ($dbh,$table,$field,$type,$value)=@_;

	my $z=_contains_field($dbh,$table,$field);
	if($z!=0) {
		return $z;
	}
	# Get the table defintiion information..

	my ($x,$r1,$r2)=_table_field_list($dbh,$table);
	if($x==0) {
		return -2;
	}
	my $cmd="create table ${table}_backup(\n";
	my $cmd2="create table ${table}(\n";
	my $ii=0;
	while($ii<@$r1) {
		$r2->[$ii]="" if !defined($r2->[$ii]);
		$cmd2.="  $r1->[$ii] $r2->[$ii],\n";
		if($ii+1==@$r1) {
			$cmd.="  $r1->[$ii] $r2->[$ii]\n";
		} else {
			$cmd.="  $r1->[$ii] $r2->[$ii],\n";
		}
		$ii++;
	}
	$cmd.=");";
	$cmd2.="  $field $type\n);";

	$dbh->do($cmd);
	$cmd="insert into ${table}_backup SELECT " . join(",",@$r1) . " FROM $table;";
	$dbh->do($cmd);
	$cmd="DROP TABLE $table;";
	$dbh->do($cmd);
	$dbh->do($cmd2);
	# Read recrods from backup to new...
	$cmd="select * from ${table}_backup;";
	my @FLIST=(@$r1,$field);
	my $flist_txt=join(",",@FLIST);
	my $rr=_get_rows($dbh,$cmd);
	my $count=0;
	for my $crec (@$rr) {
		$crec->{$field}=$value;
		$cmd="INSERT INTO $table ($flist_txt) VALUES (";
		my $cc="";
		for my $xxx (@FLIST) {
			$cc.=",'$crec->{$xxx}'";
		}
		$cmd.=substr($cc,1) . ");";
		$dbh->do($cmd);
		$count++;
	}
	$cmd="DROP TABLE ${table}_backup;";
	$dbh->do($cmd);
	return 10+$count;
}

sub _generate_request_id {
my $t=time; my $t2=rand();

	$t2 =~ s/\.//;
	return "$t$t2";
}

sub register_portal_access_request($$$$$) {
my ($self,$user,$portal,$txt,$gri_globals_handle)=@_;

	my $id=_generate_request_id();
	$txt=encode_base64($txt,"");
	my $rr=$gri_globals_handle->get_portal_details($portal);
	my $portal2=encode_base64($portal,"");
	if(defined($rr->{error})) {
		return {
			id => undef,
			error => $rr->{error}
		};
	}

	# We should have enough details to construct the record now...

	my $voters="";
	my $owners="";
	for my $c (@{$rr->{owners}}) {
		$owners.="$c||".encode_base64("==NO_COMMENTS==","").' ';
	}
	my $comments=encode_base64("==NO_COMMENTS==","");
	for my $c (@{$rr->{voters}}) {
		$voters.="$c||".encode_base64("==NO_COMMENTS==","").' ';
	}
	my $sql="INSERT INTO requests (id,type,user,object,req_comments,owners_votes,voters_votes,decision,concurrence,informed,comments) VALUES('$id','PAR','$user','$portal2','$txt','$owners','$voters','','$rr->{concur_count}','','$comments');";
	my $r=$self->do($sql);
	if(!$r) {
		return {
			id => undef,
			error => "Failed to load record in DB.",
		};
	}

	return {
		id => $id,
		error => undef,
	};
}

1;

# vim: ts=4:sw=4
